#include "bsp_mjpeg.h"
#include "debug_log.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "bsp_uart.h"
extern "C"{
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
}

// 全局变量定义（添加到文件头部）
pthread_cond_t mjpeg_encoder_exit_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mjpeg_encoder_exit_mutex = PTHREAD_MUTEX_INITIALIZER;

// 时间工具函数（替代C++ chrono）
static int64_t get_current_time_ms() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

// 创建编码器上下文（模拟构造函数）
VideoEncoderContext* video_encoder_create(const EncoderParams *params) 
{
    VideoEncoderContext *ctx = (VideoEncoderContext*)malloc(sizeof(VideoEncoderContext));
    if (!ctx) return NULL;

    // 初始化参数
    memset(ctx, 0, sizeof(VideoEncoderContext));
    memcpy(&ctx->params, params, sizeof(EncoderParams));
    ctx->video_stream_idx = -1;
    ctx->is_running = false;  // 初始化：未运行
    ctx->input_ctx = NULL;
    ctx->decoder_ctx = NULL;
    ctx->encoder_ctx = NULL;
    ctx->sws_ctx = NULL;
    ctx->output_file_hdl = NULL;

    return ctx;
}

// 初始化编码器（设备打开、编解码器初始化）
int video_encoder_init(VideoEncoderContext *ctx) {
    int ret = 0;
    const EncoderParams *params = &ctx->params;

    // --------------------------
    // 步骤1：打开输入设备（MJPG流）
    // --------------------------
    const AVInputFormat *input_fmt = av_find_input_format("v4l2");
    if (!input_fmt) {
        fprintf(stderr, "找不到V4L2输入格式\n");
        return -1;
    }
    log_info(params->print_log, "av_find_input_format ok");

    AVDictionary *input_opts = NULL;
    av_dict_set(&input_opts, "input_format", "mjpeg", 0);
    
    // 拼接视频尺寸字符串
    char size_str[32];
    snprintf(size_str, sizeof(size_str), "%dx%d", params->width, params->height);
    av_dict_set(&input_opts, "video_size", size_str, 0);
    
    char framerate_str[16];
    snprintf(framerate_str, sizeof(framerate_str), "%d", params->framerate);
    av_dict_set(&input_opts, "framerate", framerate_str, 0);
    //打开输入设备时添加缓冲区控制
    // av_dict_set(&input_opts, "video_buffer_size", "1", 0);
    // av_dict_set(&input_opts, "latency", "0", 0);

    av_dict_set(&input_opts, "timeout", "50000", 0);  // 50ms超时


    AVFormatContext *input_ctx = NULL;
    ret = avformat_open_input(&input_ctx, params->device, input_fmt, &input_opts);
    if (ret != 0) {
        log_info(params->print_log, "无法打开视频设备, ret = %d", ret);
        av_dict_free(&input_opts); 
        return ret;
    }
    ctx->input_ctx = input_ctx;
    printf("##[DEBUG] 设备已成功打开: %s\n", params->device);
    log_info(params->print_log, "avformat_open_input ok");

    // 查找流信息
    if (avformat_find_stream_info(input_ctx, NULL) < 0) {
        log_info(params->print_log, "无法获取流信息");
        return -1;
    }
    log_info(params->print_log, "avformat_find_stream_info ok");

    // 查找视频流索引
    int video_stream_idx = -1;
    for (unsigned int i = 0; i < input_ctx->nb_streams; i++) {
        AVStream *stream = input_ctx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_idx = i;
            break;
        }
    }
    if (video_stream_idx == -1) {
        log_info(params->print_log, "找不到视频流");
        return -1;
    }
    ctx->video_stream_idx = video_stream_idx;
    log_info(params->print_log, "video_stream_idx = %d", video_stream_idx);

    // --------------------------
    // 步骤2：初始化MJPG解码器（硬解优先）
    // --------------------------
    const AVCodec *mjpeg_decoder = avcodec_find_decoder_by_name("mjpeg_rkmpp");
    if (!mjpeg_decoder) {
        log_info(params->print_log, "找不到mjpeg_rkmpp硬解码器，fallback到软解码");
        mjpeg_decoder = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
        if (!mjpeg_decoder) {
            log_info(params->print_log, "找不到MJPG解码器");
            return -1;
        }
    }

    AVCodecContext *decoder_ctx = avcodec_alloc_context3(mjpeg_decoder);
    if (!decoder_ctx) {
        log_info(params->print_log, "无法分配解码器上下文");
        return -1;
    }

    // 复制参数并打开解码器
    ret = avcodec_parameters_to_context(decoder_ctx, 
        input_ctx->streams[video_stream_idx]->codecpar);
    if (ret < 0) {
        log_info(params->print_log, "无法复制解码器参数, ret = %d", ret);
        avcodec_free_context(&decoder_ctx);
        avformat_close_input(&input_ctx);
        av_dict_free(&input_opts);
        return ret;
    }

    ret = avcodec_open2(decoder_ctx, mjpeg_decoder, NULL);
    if (ret < 0) {
        log_info(params->print_log, "无法打开解码器, ret = %d", ret);
        avcodec_free_context(&decoder_ctx);
        avformat_close_input(&input_ctx);
        av_dict_free(&input_opts);
        return ret;
    }
    ctx->decoder_ctx = decoder_ctx;
    log_info(params->print_log, "解码器打开成功");

    // --------------------------
    // 步骤3：初始化编码器（H264/H265，硬编优先）
    // --------------------------
    const AVCodec *encoder = NULL;
    if (params->format == ENCODING_FORMAT_H265) {
        encoder = avcodec_find_encoder_by_name("hevc_rkmpp");
        if (!encoder) {
            log_info(params->print_log, "找不到hevc_rkmpp硬编码器，fallback到软编码");
            encoder = avcodec_find_encoder(AV_CODEC_ID_HEVC);
        }
    } else {
        encoder = avcodec_find_encoder_by_name("h264_rkmpp");
        if (!encoder) {
            log_info(params->print_log, "找不到h264_rkmpp硬编码器，fallback到软编码");
            encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
        }
    }

    if (!encoder) {
        log_info(params->print_log, "找不到指定格式的编码器");
        return -1;
    }

    AVCodecContext *encoder_ctx = avcodec_alloc_context3(encoder);
    if (!encoder_ctx) {
        log_info(params->print_log, "无法分配编码器上下文");
        return -1;
    }

    // 设置编码器参数
    if (params->width == 1920 && params->height == 1080)
    {
        #if 0
        encoder_ctx->max_b_frames = 1;                  // B帧
        encoder_ctx->gop_size = params->framerate - 5;      // GOP=帧率;25（1I帧）
        #else
        encoder_ctx->max_b_frames = 0;                  // B帧
        encoder_ctx->gop_size = 1;      // GOP=帧率（1秒2个I帧）ok
        #endif
        // encoder_ctx->keyint_min = params->framerate / 2; // 最小I帧间隔
        encoder_ctx->bit_rate = 3000000;               // 4Mbps CBR码率
        // encoder_ctx->rc_min_rate = encoder_ctx->bit_rate;
        // encoder_ctx->rc_max_rate = encoder_ctx->bit_rate;
        // encoder_ctx->rc_buffer_size = encoder_ctx->bit_rate / 2;  // 小缓冲区
        encoder_ctx->width = params->width;
        encoder_ctx->height = params->height;
        encoder_ctx->time_base = (AVRational){1, params->framerate};
        encoder_ctx->framerate = (AVRational){params->framerate, 1};
        encoder_ctx->pix_fmt = AV_PIX_FMT_NV12;  // RKMPP推荐格式
    }
    else if (params->width == 640 && params->height == 480)
    {
        #if 0
        encoder_ctx->max_b_frames = 1;                  // B帧
        encoder_ctx->gop_size = params->framerate - 5;      // GOP=帧率;25（1I帧）
        #else
        encoder_ctx->max_b_frames = 0;                  // B帧
        encoder_ctx->gop_size = 1;      // GOP=帧率（1秒2个I帧）ok
        #endif
        encoder_ctx->keyint_min = params->framerate / 2; // 最小I帧间隔
        encoder_ctx->bit_rate = 3000000;               // 4Mbps CBR码率
        encoder_ctx->rc_min_rate = encoder_ctx->bit_rate;
        encoder_ctx->rc_max_rate = encoder_ctx->bit_rate;
        encoder_ctx->rc_buffer_size = encoder_ctx->bit_rate / 2;  // 小缓冲区
        encoder_ctx->width = params->width;
        encoder_ctx->height = params->height;
        encoder_ctx->time_base = (AVRational){1, params->framerate};
        encoder_ctx->framerate = (AVRational){params->framerate, 1};
        encoder_ctx->pix_fmt = AV_PIX_FMT_NV12;  // RKMPP推荐格式
    } else {
        log_info(0,"Error: not input parms");
    }


    // 编码器选项
    AVDictionary *encode_opts = NULL;
    av_dict_set(&encode_opts, "preset", "ultrafast", 0);  // 最快编码,低压缩率，画质高，但是延迟高
    av_dict_set(&encode_opts, "tune", "zerolatency", 0);   // 低延迟优化
    av_dict_set(&encode_opts, "crf", "40", 0);   

    if (params->format == ENCODING_FORMAT_H264) {
        av_dict_set(&encode_opts, "rc_method", "cbr", 0);  // H.264 CBR
        av_dict_set(&encode_opts, "profile", "high", 0);  // 设置H.264编码配置文件
        av_dict_set(&encode_opts, "level", "4.0", 0);     // 设置H.264编码级别
    } else {
        // av_dict_set(&encode_opts, "rc_mode", "cbr", 0);    // H.265 CBR
        av_dict_set(&encode_opts, "profile", "main", 0);  // H.265主配置文件
        av_dict_set(&encode_opts, "tier", "main", 0);     // H.265主层级
    }

    ret = avcodec_open2(encoder_ctx, encoder, &encode_opts);
    if (ret < 0) {
        log_info(params->print_log, "无法打开编码器, ret = %d", ret);
        avcodec_free_context(&encoder_ctx);
        av_dict_free(&encode_opts);
        return ret;
    }
    av_dict_free(&encode_opts);
    ctx->encoder_ctx = encoder_ctx;
    log_info(params->print_log, "编码器打开成功（格式: %s）", 
        params->format == ENCODING_FORMAT_H265 ? "H265" : "H264");

    // --------------------------
    // 步骤4：初始化格式转换（MJPG->NV12）
    // --------------------------
    struct SwsContext *sws_ctx = sws_getContext(
        decoder_ctx->width, decoder_ctx->height, decoder_ctx->pix_fmt,
        encoder_ctx->width, encoder_ctx->height, encoder_ctx->pix_fmt,
        SWS_BILINEAR, NULL, NULL, NULL
    );
    if (!sws_ctx) {
        log_info(params->print_log, "无法初始化格式转换器");
        return -1;
    }
    ctx->sws_ctx = sws_ctx;

    // --------------------------
    // 步骤5：打开输出文件
    // --------------------------
    FILE *output_file_hdl = fopen(params->output_file, "wb");
    if (!output_file_hdl) {
        fprintf(stderr, "无法打开输出文件: %s\n", params->output_file);
        return -1;
    }
    ctx->output_file_hdl = output_file_hdl;

    return 0;
}

// 编码核心循环
static void video_encoder_encode_loop(VideoEncoderContext *ctx,Uart_t* uart) {
    if (!ctx->is_running) return;
    const EncoderParams *params = &ctx->params;

    // 初始化变量
    const char *format_str = (params->format == ENCODING_FORMAT_H265) ? "H265" : "H264";
    if (params->record_seconds > 0) {
        printf("开始录制%s...（%d秒）\n", format_str, params->record_seconds);
    } else {
        printf("开始无限录制%s...\n", format_str);
    }

    int64_t start_time_ms = get_current_time_ms();
    int frame_count = 0;
    bool has_valid_frame = false;  // 标记是否有有效帧

    AVPacket *input_pkt = av_packet_alloc();
    if (!input_pkt) {
        log_info(params->print_log, "无法分配输入包");
        return;
    }

    AVFormatContext *input_ctx = (AVFormatContext*)ctx->input_ctx;
    AVCodecContext *decoder_ctx = (AVCodecContext*)ctx->decoder_ctx;
    AVCodecContext *encoder_ctx = (AVCodecContext*)ctx->encoder_ctx;
    struct SwsContext *sws_ctx = (struct SwsContext*)ctx->sws_ctx;
    FILE *output_hdl = ctx->output_file_hdl;

    while (ctx->is_running) {
        // 检查录制时长（仅当record_seconds>0时）
        if (params->record_seconds > 0) {
            int64_t elapsed_ms = get_current_time_ms() - start_time_ms;
            if (elapsed_ms / 1000 >= params->record_seconds) {
                break;
            }
        }
        // 读取一帧数据
        int ret = av_read_frame(input_ctx, input_pkt);
        if (ret < 0) {
            if (ret == AVERROR_EOF) {
                log_info(params->print_log, "已到达流末尾");
                break;
            } else {
                log_info(params->print_log, "读取帧失败 %d，继续", ret);
                av_packet_unref(input_pkt);
                continue;
            }
        }
        //添加时间戳
        int64_t input_pts = input_pkt->pts;
        // 只处理视频流
        if (input_pkt->stream_index == ctx->video_stream_idx) {
            // 发送到解码器
            ret = avcodec_send_packet(decoder_ctx, input_pkt);
            if (ret < 0) {
                log_info(params->print_log, "发送包到解码器失败 %d，继续", ret);
                av_packet_unref(input_pkt);
                continue;
            }

            // 接收解码帧
            AVFrame *decoded_frame = av_frame_alloc();
            while (ret >= 0) {
                ret = avcodec_receive_frame(decoder_ctx, decoded_frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    log_info(params->print_log, "接收解码帧失败 %d，继续", ret);
                    break;
                }
                //根据输入pkt解码器设置
                if (decoded_frame->pts == AV_NOPTS_VALUE)
                {
                    decoded_frame->pts = input_pts;
                }
                
                // 分配输出帧（NV12）
                AVFrame *yuv_frame = av_frame_alloc();
                yuv_frame->format = encoder_ctx->pix_fmt;
                yuv_frame->width = encoder_ctx->width;
                yuv_frame->height = encoder_ctx->height;
                av_frame_get_buffer(yuv_frame, 32);

                // 格式转换（MJPG->NV12）
                sws_scale(sws_ctx, decoded_frame->data, decoded_frame->linesize, 0,
                         decoder_ctx->height, yuv_frame->data, yuv_frame->linesize);

                // 发送到编码器
                // yuv_frame->pts = frame_count++;
                yuv_frame->pts = decoded_frame->pts;
                ret = avcodec_send_frame(encoder_ctx, yuv_frame);
                if (ret < 0) {
                    log_info(params->print_log, "发送帧到编码器失败 %d，继续", ret);
                    av_frame_free(&yuv_frame);
                    av_frame_free(&decoded_frame);
                    continue;
                }

                // 接收编码包并写入文件
                AVPacket *output_pkt = av_packet_alloc();
                while (ret >= 0) {
                    ret = avcodec_receive_packet(encoder_ctx, output_pkt);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                        break;
                    } else if (ret < 0) {
                        log_info(params->print_log, "接收编码包失败 %d，继续", ret);
                        break;
                    }

                    // 标记有有效帧
                    has_valid_frame = true;

                    // 输出编码包信息
                    log_info(0,"编码包首地址: %p, 长度: %d\n", 
                        output_pkt->data, output_pkt->size);

                    // 写入文件
                    // fwrite(output_pkt->data, 1, output_pkt->size, output_hdl);
                    // 写入串口
                    if (output_pkt->size > 0 && uart->fd > 0 && (thread1_write_data_to_uart == 1)) {
                        int uart_ret = -1;

                        pthread_mutex_lock(&usb0_data_lock);
                        uart_ret = uart_send_h265_frame(uart->fd, output_pkt->data, output_pkt->size);
                        pthread_mutex_unlock(&usb0_data_lock);
                        if (uart_ret != 0){
                            perror("数据未发送，即将发送单原子性的下一帧");
                            break;
                        }
                                
                    }
                    av_packet_unref(output_pkt);
                }

                // 释放资源
                av_packet_free(&output_pkt);
                av_frame_free(&yuv_frame);
            }
            av_frame_free(&decoded_frame);
        }

        av_packet_unref(input_pkt);
    }

    if (ctx->is_running) {  // 若仍在运行中，才需要刷新

        // 刷新编码器剩余数据
        AVPacket *flush_pkt = av_packet_alloc();
        avcodec_send_frame(encoder_ctx, NULL);
        while (ctx->is_running) {
            int ret = avcodec_receive_packet(encoder_ctx, flush_pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
            if (ret < 0) break;

            if (flush_pkt->size > 0) {
                has_valid_frame = true;
                printf("刷新包首地址: %p, 长度: %d\n", flush_pkt->data, flush_pkt->size);
                //写入文件
                // fwrite(flush_pkt->data, 1, flush_pkt->size, output_hdl);
                // 写入串口
                if (flush_pkt->size > 0 && uart->fd > 0 && (thread1_write_data_to_uart == 1)) {
                    int uart_ret = -1;

                    pthread_mutex_lock(&usb0_data_lock);
                    uart_ret = uart_send_h265_frame(uart->fd, flush_pkt->data, flush_pkt->size);
                    pthread_mutex_unlock(&usb0_data_lock);
                    if (uart_ret != 0){
                        perror("数据未发送，即将发送单原子性的下一帧");
                        break;
                    }
                            
                }
            }
            av_packet_unref(flush_pkt);
        }

        av_packet_free(&flush_pkt);
    }
    av_packet_free(&input_pkt);

    // 关闭文件并检查是否为空
    fclose(output_hdl);
    ctx->output_file_hdl = NULL;

    // 空文件处理：无有效帧则删除
    if (!has_valid_frame) {
        log_info(params->print_log, "未编码有效帧，删除空文件");
        remove(params->output_file);
    } else {
        printf("录制完成，文件保存为：%s\n", params->output_file);
    }
}

// 线程函数包装
void* encode_thread_mjpeg(void *arg) {
    VideoEncoderContext *ctx = (VideoEncoderContext*)arg;
    if (ctx == NULL) {
        printf("错误：编码器上下文为空\n");
        return NULL;
    }
    // 标记为运行中（启动前设置）
    ctx->is_running = true;
    // 直接调用编码循环（循环内部已响应thread1_should_exit）
    video_encoder_encode_loop(ctx, g_uart_usb0);

    // 循环退出后，停止编码器
    video_encoder_stop(ctx);
    return NULL;
}

// 启动编码线程
// 启动编码线程（显式标记运行状态）
void video_encoder_start(VideoEncoderContext *ctx) {
    if (ctx->is_running) return;  // 已运行则直接返回
    ctx->is_running = true;       // 标记为运行中
    video_encoder_encode_loop(ctx, g_uart_usb0);  // 启动循环
}

// 停止编码并释放资源
void video_encoder_stop(VideoEncoderContext *ctx) {
    //1. 先检查资源是否释放问题
    int print_ok = 1;
    if (!ctx->is_running) 
        return;
    log_info(print_ok,"##[DEBUG] 开始停止编码器\n");
    // 关键：确认线程是否已退出
    ctx->is_running = false;
        // 释放格式转换器
        if (ctx->sws_ctx) {
            sws_freeContext((struct SwsContext*)ctx->sws_ctx);
            ctx->sws_ctx = NULL;
        }

        // 释放编码器和解码器
        if (ctx->encoder_ctx) {
            avcodec_free_context((AVCodecContext**)&ctx->encoder_ctx);
            ctx->encoder_ctx = NULL;
        }
        if (ctx->decoder_ctx) {
            avcodec_free_context((AVCodecContext**)&ctx->decoder_ctx);
            ctx->decoder_ctx = NULL;
        }
        if (ctx->input_ctx) {
            AVFormatContext *input_ctx = (AVFormatContext*)ctx->input_ctx;
            log_info(print_ok,"##[DEBUG] 关闭输入设备: %p\n", input_ctx);
            avformat_close_input(&input_ctx);
            ctx->input_ctx = NULL;
            log_info(print_ok,"##[DEBUG] 设备已关闭\n");
        }
        printf("##[DEBUG] 编码循环已退出\n");
}

// 销毁编码器上下文
void video_encoder_destroy(VideoEncoderContext *ctx) {
    if (!ctx) return;
    video_encoder_stop(ctx);
    free(ctx);
}